//
// Created by admin on 2023/1/30.
//

#include "select.h"
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include "./error.h"
#include "./fd.h"
#include <unistd.h>
#include <string.h>

namespace utils {

Select::Select() :
    m_epoll_fd(epoll_create(2)),
    m_event_fd(eventfd(0, 0))
{
    if ((m_epoll_fd < 0) || (m_event_fd < 0)) {
        throw IoError();
    }

    epoll_event env{};
    env.data.fd = m_event_fd;
    env.events = EPOLLIN;

    int ret = epoll_ctl(m_epoll_fd, EPOLL_CTL_ADD, m_event_fd, &env);
    if (ret < 0) {
        throw IoError();
    }
}

void Select::set_read_fd(int fd) {
    epoll_event env{};
    env.data.fd = fd;
    env.events = EPOLLIN;

    int ret = epoll_ctl(m_epoll_fd, EPOLL_CTL_ADD, fd, &env);
    if (ret < 0) {
        throw IoError();
    }
}

Select::~Select() {
    close_fd(m_epoll_fd);
    close_fd(m_event_fd);
}

ssize_t Select::read(int target_fd, void *buf, size_t size) {

    epoll_event envs[2];

    int n = epoll_wait(m_epoll_fd, envs, 2, -1);
    if (n <= 0) {
        throw IoError();
    }

    for (int i = 0; i < n; i ++) {
        int fd = envs[i].data.fd;
        if (fd == target_fd) {
            break;
        }
        if (fd == m_event_fd) {
            throw IllegalError("use closed");
        }
    }

    ssize_t ret =::read(target_fd, buf, size);

    if (ret <= 0) {
        throw IoError();
    }

    return ret;
}

void Select::exit_block() {
    ::eventfd_write(m_event_fd, 1);
}


}












